package service

import (
	"context"
	"fmt"
	"sort"
	"strconv"

	"github.com/pkg/errors"

	"bilinadmin/pkg/util"

	pb "bilinadmin/api/pb/server"
	"bilinadmin/internal/dao"
	"bilinadmin/internal/model"
	"bilinadmin/internal/object"
	"bilinadmin/pkg/lueerror"
)

type bsService struct {
	*Service
	name string
}

var bSrv *bsService

// InitBrandService 初始化品牌逻辑层
func InitBrandService() {
	bSrv = &bsService{
		Service: service,
		name:    "brand_store_service",
	}
}

// GetBrandService 获取品牌逻辑层
func GetBrandService() *bsService {
	return bSrv
}

// SayHello test say hello
func (srv *bsService) SayHello(ctx context.Context, name string, id int64) (string, lueerror.LueError) {
	if id == -1 {
		return "", lueerror.InvalidStatus(errors.New("invalid id"))
	}
	msg := fmt.Sprintf("hello %s, id: %d", name, id)
	srv.logger.For(ctx).Info(msg)
	return msg, nil
}

func (srv *bsService) createBrandAndSuEmp(ctx context.Context, name, logo, desc, accountId, userName, mobile, email, origin string, bizType int8) (*object.BrandEmp, lueerror.LueError) {
	ret, err := dao.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		var err error
		// 品牌
		brand := model.NewBrand(name, logo, desc, accountId, bizType)
		brand.Id, err = dao.GetBrandDao().CreateBrand(ctx, brand)
		if err != nil {
			return nil, lueerror.DbErr(err)
		}
		// 员工
		emp := model.NewEmployee(brand.Id, userName, mobile, email, accountId, model.EmployeeStatusOK, util.GetNow())
		emp.Id, err = dao.GetEmployeeDao().CreateEmployee(ctx, emp)
		if err != nil {
			return nil, lueerror.DbErr(err)
		}
		// 更新超级管理员
		brand.AssignSuperAdmin(emp.Id)
		// 来自星团的请求，自动开通星团
		if origin == "bg" {
			brand.OpenBg(emp.Id)
		}
		err = dao.GetBrandDao().UpdateBrandInfo(ctx, brand)
		if err != nil {
			return nil, err
		}
		ret := &object.BrandEmp{
			BrandId:   brand.Id,
			EmpId:     emp.Id,
			BrandName: brand.Name,
			Logo:      brand.Logo,
			Desc:      brand.Description,
			BizType:   brand.BizType,
			EmpName:   emp.Name,
		}
		return ret, nil
	})
	if err != nil {
		return nil, parseLueerror(err)
	}
	return ret.(*object.BrandEmp), nil
}

// CreateBrand 创建名牌v1
func (srv *bsService) CreateBrand(ctx context.Context, req *pb.BrandCreateRequest, accId, userName, mobile, email string) (*pb.BrandResponse, lueerror.LueError) {
	if req == nil {
		return nil, nil
	}
	ret, errL := srv.createBrandAndSuEmp(ctx, req.Name, req.Logo, req.Desc, accId, userName, mobile, email, req.Origin, 0)
	if errL != nil {
		return nil, errL
	}
	// 发送创建消息
	dao.GetMessageDao().SendBrandStoreCreateMsg(ctx, ret.BrandId, 0)
	return &pb.BrandResponse{
		BrandId: ret.BrandId,
		EmpId:   ret.EmpId,
		Name:    ret.BrandName,
		Logo:    ret.Logo,
		Desc:    ret.Desc,
		BizType: pb.BizTypeEnum(ret.BizType),
	}, nil
}

// UpdateBrand 更新品牌
func (srv *bsService) UpdateBrand(ctx context.Context, brandId int64, req *pb.BrandUpdateReq) (*pb.BrandInfoReply, lueerror.LueError) {
	if req == nil {
		return nil, nil
	}
	brand, errL := srv.getBrandById(ctx, brandId)
	if errL != nil {
		return nil, errL
	}
	brand.UpdateInfo(req.Name, req.Logo, req.Desc)
	err := dao.GetBrandDao().UpdateBrandInfo(ctx, brand)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	return srv.toBrandInfoReply(brand), nil
}

// GetBrand 获取品牌信息
func (srv *bsService) GetBrand(ctx context.Context, brandId int64) (*pb.BrandInfoReply, lueerror.LueError) {
	brand, errL := srv.getBrandById(ctx, brandId)
	if errL != nil {
		return nil, errL
	}
	return srv.toBrandInfoReply(brand), nil
}

// GetBrandsOfAccount 根据账号获取品牌列表
func (srv *bsService) GetBrandsOfAccount(ctx context.Context, accId string) (*pb.BrandsGetResponse, lueerror.LueError) {
	emps, err := dao.GetEmployeeDao().GetEmployeeRelatedTo(ctx, accId)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	brandIds := make([]int64, len(emps))
	brandEmpMap := make(map[int64]int64, len(emps))
	for idx, emp := range emps {
		brandIds[idx] = emp.BrandId
		brandEmpMap[emp.BrandId] = emp.Id
	}
	brands, err := dao.GetBrandDao().GetBrandByIds(ctx, brandIds)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	rets := make([]*pb.BrandResponse, len(brands))
	for idx, br := range brands {
		empId, _ := brandEmpMap[br.Id]
		rets[idx] = &pb.BrandResponse{
			BrandId: br.Id,
			EmpId:   empId,
			Name:    br.Name,
			Logo:    br.Logo,
			Desc:    br.Description,
			BizType: pb.BizTypeEnum(br.BizType),
		}
	}
	return &pb.BrandsGetResponse{
		Brands: rets,
	}, nil
}

func (srv *bsService) getBrandById(ctx context.Context, brandId int64) (*model.Brand, lueerror.LueError) {
	brand, err := dao.GetBrandDao().GetBrandById(ctx, brandId)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if brand == nil {
		return nil, lueerror.InvalidBrand(errors.New("品牌不存在"))
	}
	if !brand.IsValid() {
		return nil, lueerror.InvalidBrand(fmt.Errorf("品牌 %d 无效", brand.Id))
	}
	return brand, nil
}

func (srv *bsService) toBrandInfoReply(brand *model.Brand) *pb.BrandInfoReply {
	if brand == nil {
		return nil
	}
	return &pb.BrandInfoReply{
		BrandId:    brand.Id,
		Name:       brand.Name,
		Logo:       brand.Logo,
		Desc:       brand.Description,
		BizType:    brand.GetBizType(),
		BgOpened:   brand.HasBg(),
		MallOpened: brand.HasMall(),
	}
}

// GetBrandInfoById 根据id 获取品牌信息
func (srv *bsService) GetBrandInfoById(ctx context.Context, brandId int64) (*pb.BrandInfoReply, lueerror.LueError) {
	brand, errL := srv.getBrandById(ctx, brandId)
	if errL != nil {
		return nil, errL
	}
	return srv.toBrandInfoReply(brand), nil
}

func (srv *bsService) toStoreInfoReply(store *model.Store) *pb.StoreInfoReply {
	if store == nil {
		return nil
	}
	return &pb.StoreInfoReply{
		BrandId:       store.BrandId,
		StoreId:       store.Id,
		StoreName:     store.Name,
		ContactName:   store.ContactName,
		ContactMobile: store.ContactMobile,
		BusinessScope: store.BusinessScope,
		DistrictCode:  store.DistrictCode,
		RegionCode:    store.RegionCode,
		Address:       store.Address,
		Latitude:      store.Latitude,
		Longitude:     store.Longitude,
	}
}

// OpenMall 开通星团
func (srv *bsService) OpenMall(ctx context.Context, brandId, empId int64, p *pb.OpenMallReq) lueerror.LueError {
	if p == nil {
		return nil
	}
	brand, errL := srv.getBrandById(ctx, brandId)
	if errL != nil {
		return errL
	}
	if !brand.HasMall() {
		if err := brand.SetBizType(p.BizType); err != nil {
			return lueerror.InvalidRequest(errors.New("经营模式类型错误"))
		}
		brand.OpenMall(empId)
		if err := dao.GetBrandDao().UpdateBrandInfo(ctx, brand); err != nil {
			return lueerror.DbErr(err)
		}
	}
	return nil
}

// CreateStore 创建门店
func (srv *bsService) CreateStore(ctx context.Context, brandId, empId int64, p *pb.StoreCreateReq) (*pb.StoreInfoReply, lueerror.LueError) {
	if p == nil {
		return nil, nil
	}
	brand, errL := srv.getBrandById(ctx, brandId)
	if errL != nil {
		return nil, errL
	}
	// 品牌开通商城才可以创建门店
	if !brand.HasMall() {
		return nil, lueerror.InvalidMall(fmt.Errorf("品牌 %d 未开通商城", brand.Id))
	}
	// 创建门店
	var err error
	store := model.NewStore(brand.Id, p.StoreName, p.ContactName, p.ContactMobile, p.BusinessScope, p.DistrictCode, p.RegionCode, p.Address, p.Latitude, p.Longitude, empId)
	store.Id, err = dao.GetStoreDao().CreateStore(ctx, store)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	// 发送更新消息
	dao.GetMessageDao().SendStoreUpdateMsg(ctx, store)
	// 发送创建消息
	dao.GetMessageDao().SendBrandStoreCreateMsg(ctx, brandId, store.Id)
	return srv.toStoreInfoReply(store), nil
}

func (srv *bsService) getStoreById(ctx context.Context, storeId int64) (*model.Store, lueerror.LueError) {
	store, err := dao.GetStoreDao().GetStoreById(ctx, storeId)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if store == nil {
		return nil, lueerror.InvalidStore(errors.New("门店不存在"))
	}
	if !store.IsValid() {
		return nil, lueerror.InvalidStore(fmt.Errorf("门店 %d 不可用状态", store.Id))
	}
	return store, nil
}

// GetStoreInfoById 根据id获取门店信息
func (srv *bsService) GetStoreInfoById(ctx context.Context, brandId, storeId int64) (*pb.StoreInfoReply, lueerror.LueError) {
	store, errL := srv.getStoreById(ctx, storeId)
	if errL != nil {
		return nil, errL
	}
	if store.BrandId != brandId {
		return nil, lueerror.InvalidStore(fmt.Errorf("门店%d 品牌不一致", storeId))
	}
	return srv.toStoreInfoReply(store), nil
}

func (srv *bsService) toStoreAbstractInfo(s *model.Store) *pb.StoreAbstractInfo {
	if s == nil {
		return nil
	}
	return &pb.StoreAbstractInfo{
		BrandId:       s.BrandId,
		StoreId:       s.Id,
		StoreName:     s.Name,
		ContactName:   s.ContactName,
		ContactMobile: s.ContactMobile,
		BusinessScope: s.BusinessScope,
		Status:        s.GetStatus(),
	}
}

// SearchStoresList 获取门店列表
func (srv *bsService) SearchStoresList(ctx context.Context, brandId int64, busScope, status, search string) (*pb.StoresInfoReply, lueerror.LueError) {
	var stores []*model.Store
	var err error
	if busScope == "" && status == "" && search == "" {
		// 按品牌获取门店列表
		stores, _, err = dao.GetStoreDao().GetStoresAbstractByBrand(ctx, brandId, object.NewPagination(0, 0))
	} else {
		st := model.GetInnerStoreStatus(status)
		// 省级编码后四位为0，市级编码后两位为00
		busScope = util.DistrictParse(busScope)
		stores, err = dao.GetStoreDao().SearchStoresAbstract(ctx, brandId, st, busScope, search)
	}
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	rets := make([]*pb.StoreAbstractInfo, len(stores))
	for idx, s := range stores {
		rets[idx] = srv.toStoreAbstractInfo(s)
	}
	return &pb.StoresInfoReply{Stores: rets}, nil
}

// GetStoreListByBrand 根据品牌获取门店列表
func (srv *bsService) GetStoreListByBrand(ctx context.Context, brandId int64, pg *object.Pagination) (*pb.StoresInfoReply, lueerror.LueError) {
	stores, total, err := dao.GetStoreDao().GetStoresAbstractByBrand(ctx, brandId, pg)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	storeInfos := make([]*pb.StoreAbstractInfo, len(stores))
	for idx, st := range stores {
		storeInfos[idx] = srv.toStoreAbstractInfo(st)
	}
	return &pb.StoresInfoReply{
		Total:    total,
		PageNo:   pg.PageNo,
		PageSize: pg.PageSize,
		Stores:   storeInfos,
	}, nil
}

// UpdateStore 更新门店
func (srv *bsService) UpdateStore(ctx context.Context, p *pb.StoreUpdateReq) (*pb.StoreInfoReply, lueerror.LueError) {
	if p == nil {
		return nil, nil
	}
	store, errL := srv.getStoreById(ctx, p.StoreId)
	if errL != nil {
		return nil, errL
	}
	store.Update(p.StoreName, p.ContactName, p.ContactMobile, p.BusinessScope, p.DistrictCode, p.RegionCode, p.Address, p.Latitude, p.Longitude)
	err := dao.GetStoreDao().UpdateStore(ctx, store)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	// 发送更新消息
	dao.GetMessageDao().SendStoreUpdateMsg(ctx, store)
	return srv.toStoreInfoReply(store), nil
}

// GetStoreById 获取门店详情
func (srv *bsService) GetStoreById(ctx context.Context, brandId, storeId int64) (*pb.StoreInfoReply, lueerror.LueError) {
	store, err := srv.getStoreById(ctx, storeId)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if store == nil || store.BrandId != brandId {
		return nil, lueerror.StoreNotExist(fmt.Errorf("品牌下 %d 无门店 %d", brandId, storeId))
	}
	return srv.toStoreInfoReply(store), nil
}

// CreateOrUpdateInventory 创建或更新仓库
func (srv *bsService) CreateOrUpdateInventory(ctx context.Context, p *pb.WarehouseUpdateMessage) lueerror.LueError {
	if p == nil {
		return nil
	}
	_, err := dao.Transaction(ctx, func(ctx context.Context) (interface{}, error) {
		// 先产线仓库
		inv, err := dao.GetInventoryDao().LockInventoryById(ctx, p.Id)
		if err != nil {
			return nil, lueerror.DbErr(err)
		}
		cityCode := strconv.FormatInt(p.CityCode, 10)
		// 新建仓库
		if inv == nil {
			inv = model.NewInventory(p.Id, p.SgId, p.StoreId, p.Name, p.Contact, p.Mobile, cityCode, p.RegionCode, p.Address, p.Latitude, p.Longitude, p.EmpId)
			err = dao.GetInventoryDao().CreateInventory(ctx, inv)
			if err != nil {
				return nil, lueerror.DbErr(err)
			}
		} else {
			inv.Update(p.Name, p.Contact, p.Mobile, cityCode, p.RegionCode, p.Address, p.Latitude, p.Longitude, p.EmpId)
			err = dao.GetInventoryDao().UpdateInventory(ctx, inv)
			if err != nil {
				return nil, lueerror.DbErr(err)
			}
		}
		return nil, nil
	})
	return parseLueerror(err)
}

// GetStoreNamesByIds 根据ids获取门店列表
func (srv *bsService) GetStoreNamesByIds(ctx context.Context, storeIds []int64) (*pb.StoreNamesReply, lueerror.LueError) {
	if len(storeIds) == 0 {
		return &pb.StoreNamesReply{
			Stores: []*pb.StoreNames{},
		}, nil
	}
	stores, err := dao.GetStoreDao().GetStoresAbstractByIds(ctx, storeIds)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	rStores := make([]*pb.StoreNames, len(stores))
	for idx, s := range stores {
		rStores[idx] = &pb.StoreNames{
			StoreId:   s.Id,
			StoreName: s.Name,
		}
	}
	return &pb.StoreNamesReply{
		Stores: rStores,
	}, nil
}

func (srv *bsService) getAgentStore(ctx context.Context, brandId int64, lat, lng float64) (*model.Store, lueerror.LueError) {
	// 查询行政区域编码
	addr, err := dao.GetBilinVendorDao().LocationToAddress(ctx, lat, lng)
	if err != nil {
		return nil, lueerror.LBSErr(err)
	}
	disCode := addr.DistrictCode
	// 一般而言，代理模式，同一个服务区域内只有一家门店
	// 先按区查找
	stores, err := dao.GetStoreDao().GetStoreNamesByBusScope(ctx, brandId, disCode)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if len(stores) > 0 {
		return stores[0], nil
	}
	// 按市级查找
	stores, err = dao.GetStoreDao().GetStoreNamesByBusScope(ctx, brandId, util.ToCityCode(disCode))
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if len(stores) > 0 {
		return stores[0], nil
	}
	// 按省级查找
	stores, err = dao.GetStoreDao().GetStoreNamesByBusScope(ctx, brandId, util.ToProvinceCode(disCode))
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if len(stores) > 0 {
		return stores[0], nil
	}
	return nil, nil
}

func (srv *bsService) getStoresByLocations(ctx context.Context, brandId int64, lat, lng float64) ([]*model.Store, lueerror.LueError) {
	stores, err := dao.GetStoreDao().GetStoresLocationByBrandId(ctx, brandId)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	if len(stores) == 0 {
		return nil, nil
	}
	// 获取两点之间距离
	from := &object.Location{lat, lng}
	tos := make([]*object.Location, len(stores))
	type storeDistance struct {
		store    *model.Store
		distance float64
	}
	sds := make([]*storeDistance, len(stores))
	for idx, s := range stores {
		tos[idx] = &object.Location{
			Lat: s.Latitude,
			Lng: s.Longitude,
		}
		sds[idx] = &storeDistance{
			store:    s,
			distance: 0,
		}
	}
	disArr, err := dao.GetBilinVendorDao().GetDistanceOfLocations(ctx, from, tos)
	if err != nil {
		return nil, lueerror.LBSErr(err)
	}
	for idx, dis := range disArr {
		sds[idx].distance = dis
	}
	// 按距离排序
	sort.Slice(sds, func(i, j int) bool {
		return sds[i].distance < sds[j].distance
	})
	for idx, s := range sds {
		stores[idx] = s.store
	}
	return stores, nil
}

// GetStoresByLocation 根据位置获取门店
func (srv *bsService) GetStoresByLocation(ctx context.Context, brandId int64, lat, lng float64) (*pb.GetStoresByGeoReply, lueerror.LueError) {
	brand, errL := srv.getBrandById(ctx, brandId)
	if errL != nil {
		return nil, errL
	}
	// 自营模式不返回任何信息
	if brand.BizType == model.BrandBizTypeSelfRun {
		return &pb.GetStoresByGeoReply{}, nil
	}
	// 经销商，只有一家门店
	if brand.BizType == model.BrandBizTypeAgent {
		store, errL := srv.getAgentStore(ctx, brandId, lat, lng)
		if errL != nil {
			return nil, errL
		}
		if store == nil {
			// 直接填充品牌信息
			return &pb.GetStoresByGeoReply{
				AgentStore: &pb.StoreNames{
					StoreId:   0,
					StoreName: brand.Name,
				},
			}, nil
		}
		return &pb.GetStoresByGeoReply{
			AgentStore: &pb.StoreNames{
				StoreId:   store.Id,
				StoreName: store.Name,
			},
		}, nil
	}
	// 平台模式
	if brand.BizType == model.BrandBizTypePlatform {
		stores, errL := srv.getStoresByLocations(ctx, brandId, lat, lng)
		if errL != nil {
			return nil, errL
		}
		rStores := make([]*pb.StoreNames, len(stores))
		for idx, s := range stores {
			rStores[idx] = &pb.StoreNames{
				StoreId:   s.Id,
				StoreName: s.Name,
			}
		}
		return &pb.GetStoresByGeoReply{
			PlatStores: rStores,
		}, nil
	}
	return nil, nil
}

// GetBrandNamesByIds 品牌名列表
func (srv *bsService) GetBrandNamesByIds(ctx context.Context, ids []int64) (*pb.BrandNamesReply, lueerror.LueError) {
	if len(ids) == 0 {
		return nil, nil
	}
	brands, err := dao.GetBrandDao().GetBrandByIds(ctx, ids)
	if err != nil {
		return nil, lueerror.DbErr(err)
	}
	rBrands := make([]*pb.BrandName, len(brands))
	for idx, b := range brands {
		rBrands[idx] = &pb.BrandName{
			BrandId:   b.Id,
			BrandName: b.Name,
		}
	}
	return &pb.BrandNamesReply{Brands: rBrands}, nil
}
